In [ ]:
epochs = 10
# We don't use the whole dataset for efficiency purpose, but feel free to increase these numbers
n_train_items = 640
n_test_items = 640

भाग X - MNIST पर सुरक्षित प्रशिक्षण और मूल्यांकन (Secure Training and Evaluation on MNIST)

मशीन लर्निंग को सेवा समाधान (MLaaS) के रूप में बनाते समय, कंपनी को अपने मॉडल को प्रशिक्षित करने के लिए अन्य भागीदारों से डेटा तक पहुंच का अनुरोध करने की आवश्यकता हो सकती है। स्वास्थ्य या वित्त में, मॉडल और डेटा दोनों ही बहुत महत्वपूर्ण हैं: मॉडल पैरामीटर एक व्यावसायिक संपत्ति है, जबकि डेटा व्यक्तिगत डेटा है जिसे कसकर विनियमित किया जाता है।

इस संदर्भ में, एक संभव समाधान मॉडल और डेटा दोनों को एन्क्रिप्ट करना और एन्क्रिप्टेड मूल्यों से अधिक मशीन लर्निंग मॉडल को प्रशिक्षित करना है। यह गारंटी देता है कि कंपनी उदाहरण के लिए रोगियों के मेडिकल रिकॉर्ड तक नहीं पहुंचेगी और स्वास्थ्य सुविधाएं उस मॉडल का निरीक्षण नहीं कर पाएंगी जिसमें वे योगदान करते हैं। कई एन्क्रिप्शन योजनाएं मौजूद हैं जो एन्क्रिप्ट किए गए डेटा पर गणना करने की अनुमति देती हैं, जिनमें से सिक्योर मल्टी-पार्टी कम्प्यूटेशन (एसएमएमसी), होमोमोर्फिक एन्क्रिप्शन (एफएचई / एसएचई) और कार्यात्मक एन्क्रिप्शन (एफई)। हम यहां मल्टी-पार्टी कम्प्यूटेशन (जिसे ट्यूटोरियल 5 में पेश किया गया है) पर ध्यान केंद्रित करेंगे, जिसमें निजी एडिटिव शेयरिंग शामिल हैं और क्रिप्टो प्रोटोकॉल SecureNN और SPDZ पर निर्भर हैं।

इस ट्यूटोरियल की सटीक सेटिंग निम्न है: इस बात पर विचार करें कि आप सर्वर हैं और आप n कार्यकर्ताओं द्वारा रखे गए कुछ डेटा पर अपने मॉडल को प्रशिक्षित करना चाहेंगे। सर्वर अपने गुप्त मॉडल को साझा करता है और प्रत्येक शेयर को एक कार्यकर्ता को भेजता है। कार्यकर्ता भी अपने डेटा को साझा करते हैं और उनके बीच आदान-प्रदान करते हैं। कॉन्फ़िगरेशन में हम अध्ययन करेंगे, 2 श्रमिक हैं: एलिस(alice) और बॉब(bob)। शेयरों का आदान-प्रदान करने के बाद, उनमें से प्रत्येक के पास अब अपने स्वयं के शेयरों में से एक, दूसरे श्रमिक का एक हिस्सा और मॉडल का एक हिस्सा है। कम्प्यूटेशन अब उपयुक्त क्रिप्टो प्रोटोकॉल का उपयोग करके मॉडल को निजी तौर पर प्रशिक्षित करना शुरू कर सकता है। एक बार मॉडल को प्रशिक्षित करने के बाद, सभी शेयरों को इसे डिक्रिप्ट करने के लिए सर्वर पर वापस भेजा जा सकता है। यह निम्नलिखित आकृति के साथ चित्रित किया गया है:

इस प्रक्रिया का एक उदाहरण देने के लिए, मान लें कि ऐलिस(alice) और बॉब(bob) दोनों MNIST डेटासेट का एक हिस्सा रखते हैं और एक मॉडल को डिजिटली वर्गीकरण करने के लिए प्रशिक्षित करते हैं!

लेखक:

अनुवादक:

1. MNIST पर एन्क्रिप्टेड प्रशिक्षण डेमो

आयात और प्रशिक्षण विन्यास


In [ ]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms

import time

यह वर्ग प्रशिक्षण के लिए सभी हाइपर-मापदंडों का वर्णन करता है। ध्यान दें कि वे सभी यहां सार्वजनिक हैं।


In [ ]:
class Arguments():
    def __init__(self):
        self.batch_size = 64
        self.test_batch_size = 64
        self.epochs = epochs
        self.lr = 0.02
        self.seed = 1
        self.log_interval = 1 # Log info at each batch
        self.precision_fractional = 3

args = Arguments()

_ = torch.manual_seed(args.seed)

यहाँ PySyft आयात हैं। हम दो दूरस्थ श्रमिकों से कनेक्ट होते हैं जिन्हें alice और bob कहा जाता है और crypto_provider नामक एक अन्य कार्यकर्ता से अनुरोध करते हैं जो हमें उन सभी क्रिप्टो प्राइमेटिव्स प्रदान करता है जो हमें आवश्यकता हो सकती हैं।


In [ ]:
import syft as sy  # import the Pysyft library
hook = sy.TorchHook(torch)  # hook PyTorch to add extra functionalities like Federated and Encrypted Learning

# simulation functions
def connect_to_workers(n_workers):
    return [
        sy.VirtualWorker(hook, id=f"worker{i+1}")
        for i in range(n_workers)
    ]
def connect_to_crypto_provider():
    return sy.VirtualWorker(hook, id="crypto_provider")

workers = connect_to_workers(n_workers=2)
crypto_provider = connect_to_crypto_provider()

एक्सेस और सीक्रेट शेयर डेटा प्राप्त करना

यहां हम एक उपयोगिता फ़ंक्शन का उपयोग कर रहे हैं, जो निम्नलिखित व्यवहार को अनुकरण करता है: हम मानते हैं कि MNIST डेटासेट उन हिस्सों में वितरित किया जाता है जिनमें से प्रत्येक हमारे किसी कार्यकर्ता द्वारा आयोजित किया जाता है। श्रमिक तब अपने डेटा को बैचों में विभाजित करते हैं और अपने गुप्त डेटा को एक दूसरे के बीच साझा करते हैं। लौटाई गई अंतिम वस्तु इन गुप्त साझा बैचों पर एक पुनरावृत्ति है, जिसे हम निजी डेटा लोडर(private data loader) कहते हैं। ध्यान दें कि प्रक्रिया के दौरान स्थानीय कार्यकर्ता (अर्थात हम) की कभी डेटा तक पहुंच नहीं थी।

हम निजी डाटासेट के रूप में एक प्रशिक्षण और परीक्षण प्राप्त करते हैं, और इनपुट और लेबल दोनों गुप्त साझा होते हैं।


In [ ]:
def get_private_data_loaders(precision_fractional, workers, crypto_provider):
    
    def one_hot_of(index_tensor):
        """
        Transform to one hot tensor
        
        Example:
            [0, 3, 9]
            =>
            [[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
             [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
             [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]]
            
        """
        onehot_tensor = torch.zeros(*index_tensor.shape, 10) # 10 classes for MNIST
        onehot_tensor = onehot_tensor.scatter(1, index_tensor.view(-1, 1), 1)
        return onehot_tensor
        
    def secret_share(tensor):
        """
        Transform to fixed precision and secret share a tensor
        """
        return (
            tensor
            .fix_precision(precision_fractional=precision_fractional)
            .share(*workers, crypto_provider=crypto_provider, requires_grad=True)
        )
    
    transformation = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,), (0.3081,))
    ])
    
    train_loader = torch.utils.data.DataLoader(
        datasets.MNIST('../data', train=True, download=True, transform=transformation),
        batch_size=args.batch_size
    )
    
    private_train_loader = [
        (secret_share(data), secret_share(one_hot_of(target)))
        for i, (data, target) in enumerate(train_loader)
        if i < n_train_items / args.batch_size
    ]
    
    test_loader = torch.utils.data.DataLoader(
        datasets.MNIST('../data', train=False, download=True, transform=transformation),
        batch_size=args.test_batch_size
    )
    
    private_test_loader = [
        (secret_share(data), secret_share(target.float()))
        for i, (data, target) in enumerate(test_loader)
        if i < n_test_items / args.test_batch_size
    ]
    
    return private_train_loader, private_test_loader
    
    
private_train_loader, private_test_loader = get_private_data_loaders(
    precision_fractional=args.precision_fractional,
    workers=workers,
    crypto_provider=crypto_provider
)

मॉडल विनिर्देश

यहां वह मॉडल है जिसका हम उपयोग करेंगे, यह एक सरल मॉडल है, लेकिन यह MNIST पर यथोचित प्रदर्शन करने के लिए साबित हुआ है


In [ ]:
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(28 * 28, 128)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, 10)

    def forward(self, x):
        x = x.view(-1, 28 * 28)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

प्रशिक्षण और परीक्षण कार्य

प्रशिक्षण लगभग हमेशा की तरह किया जाता है, वास्तविक अंतर यह है कि हम नकारात्मक लॉग-संभावना(negative log-likelihood)(PyTorch में F.nll_loss) की तरह नुकसान का उपयोग नहीं कर सकते क्योंकि यह SMPC के साथ इन कार्यों को पुन: पेश करने के लिए काफी जटिल है। इसके बजाय, हम एक सरल माध्य स्क्वायर त्रुटि हानि(mean square error loss) का उपयोग करते हैं।


In [ ]:
def train(args, model, private_train_loader, optimizer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(private_train_loader): # <-- now it is a private dataset
        start_time = time.time()
        
        optimizer.zero_grad()
        
        output = model(data)
        
        # loss = F.nll_loss(output, target)  <-- not possible here
        batch_size = output.shape[0]
        loss = ((output - target)**2).sum().refresh()/batch_size
        
        loss.backward()
        
        optimizer.step()

        if batch_idx % args.log_interval == 0:
            loss = loss.get().float_precision()
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}\tTime: {:.3f}s'.format(
                epoch, batch_idx * args.batch_size, len(private_train_loader) * args.batch_size,
                100. * batch_idx / len(private_train_loader), loss.item(), time.time() - start_time))

परीक्षण फ़ंक्शन नहीं बदलता है!


In [ ]:
def test(args, model, private_test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in private_test_loader:
            start_time = time.time()
            
            output = model(data)
            pred = output.argmax(dim=1)
            correct += pred.eq(target.view_as(pred)).sum()

    correct = correct.get().float_precision()
    print('\nTest set: Accuracy: {}/{} ({:.0f}%)\n'.format(
        correct.item(), len(private_test_loader)* args.test_batch_size,
        100. * correct.item() / (len(private_test_loader) * args.test_batch_size)))

चलो प्रशिक्षण का शुभारंभ करें!

यहाँ क्या हो रहा है, इसके बारे में कुछ नोट्स। सबसे पहले, हम अपने श्रमिकों के सभी मॉडल मापदंडों को गुप्त रखते हैं। दूसरा, हम ऑप्टिमाइज़र(optimizer) के हाइपरपरमेटर्स को निश्चित परिशुद्धत(fixed precision) में बदलते हैं। ध्यान दें कि हमें उन्हें साझा करने की आवश्यकता नहीं है क्योंकि वे हमारे संदर्भ में सार्वजनिक हैं, लेकिन गुप्त साझा मूल्यों के रूप में परिमित क्षेत्रों में रहते हैं, फिर भी हमें निरंतर संचालन करने के लिए .fixed_precision का उपयोग करके परिमित क्षेत्रों में ले जाने की आवश्यकता है। वजन अद्यतन की तरह।
$W \leftarrow W - \alpha * \Delta W$


In [ ]:
model = Net()
model = model.fix_precision().share(*workers, crypto_provider=crypto_provider, requires_grad=True)

optimizer = optim.SGD(model.parameters(), lr=args.lr)
optimizer = optimizer.fix_precision() 

for epoch in range(1, args.epochs + 1):
    train(args, model, private_train_loader, optimizer, epoch)
    test(args, model, private_test_loader)

अब अवलोकन करें! आप 100% एन्क्रिप्टेड प्रशिक्षण का उपयोग करके, और सिर्फ MNIST डेटासेट के एक छोटे से अंश का उपयोग करके 75% सटीकता प्राप्त करते हैं!

2. चर्चा

आइए हम जो कुछ भी करते हैं, उसका विश्लेषण करके एन्क्रिप्टेड प्रशिक्षण की शक्ति पर करीब से नज़र डालें।

2.1 गणना का समय

पहली बात जाहिर है कि कार्यकारी समय है! जैसा कि आपने निश्चित रूप से देखा है, यह सादे पाठ प्रशिक्षण की तुलना में बहुत धीमा है। विशेष रूप से, 64 आइटमों के 1 बैच पर एक पुनरावृत्ति 3.2s लेता है, जबकि शुद्ध PyTorch में केवल 13ms। जबकि यह एक अवरोधक की तरह लग सकता है, बस याद रखें कि यहां सब कुछ दूरस्थ रूप से और एन्क्रिप्टेड दुनिया में हुआ था: एक भी डेटा आइटम का खुलासा नहीं किया गया है। अधिक विशेष रूप से, एक आइटम को संसाधित करने का समय 50ms है जो कि बुरा नहीं है। वास्तविक प्रश्न यह विश्लेषण करना है कि एन्क्रिप्टेड प्रशिक्षण की आवश्यकता कब है और जब केवल एन्क्रिप्टेड भविष्यवाणी पर्याप्त है। एक भविष्यवाणी करने के लिए 50ms उदाहरण के लिए एक उत्पादन-तैयार परिदृश्य में पूरी तरह से स्वीकार्य है!

एक मुख्य अड़चन महंगा सक्रियण कार्यों का उपयोग है: एसएमपीसी(SMPC) के साथ रिले सक्रियण(relu activation) बहुत महंगा है क्योंकि यह निजी तुलना और SecureNN प्रोटोकॉल का उपयोग करता है। एक दृष्टांत के रूप में, यदि हम रिले को एक द्विघात सक्रियण से प्रतिस्थापित करते हैं क्योंकि यह क्रिप्टोनेट्स(CryptoNets) जैसे एन्क्रिप्टेड संगणना पर कई पत्रों में किया जाता है, तो हम 3.2s से 1.2s में देते हैं।

एक सामान्य नियम के रूप में, दूर ले जाने के लिए महत्वपूर्ण विचार है कि क्या एन्क्रिप्ट करना आवश्यक है, और यह ट्यूटोरियल आपको दिखाता है कि यह कितना सरल हो सकता है।

2.2 SMPC के साथ Backpropagation

आपको आश्चर्य हो सकता है कि हम बैकप्रोपैजेशन और ग्रेडिएंट अपडेट कैसे करते हैं, हालांकि हम परिमित क्षेत्रों में पूर्णांक के साथ काम कर रहे हैं। ऐसा करने के लिए, हमने AutogradTensor नामक एक नया syft टेंसर विकसित किया है। इस ट्यूटोरियल ने इसका गहनता से उपयोग किया, हालाँकि आपने इसे नहीं देखा होगा! आइए एक मॉडल के वजन को प्रिंट करके इसकी जांच करें:


In [ ]:
model.fc3.bias

And a data item:


In [ ]:
first_batch, input_data = 0, 0
private_train_loader[first_batch][input_data]

जैसा कि आप देख सकते हैं, AutogradTensor वहाँ है! यह torch wrapper और FixedPrecisionTensor के बीच रहता है जो दर्शाता है कि मान अब परिमित क्षेत्रों में हैं। इस AutogradTensor का लक्ष्य कंप्यूटेशन ग्राफ को संचित करना है जब एन्क्रिप्टेड मानों पर संचालन किया जाता है। यह उपयोगी है क्योंकि बैकप्रॉपैगैशन के लिए बैकवर्ड कॉल करते समय, यह AutogradTensor उन सभी पिछड़े कार्यों को ओवरराइड करता है जो एन्क्रिप्टेड कम्प्यूटेशन के साथ संगत नहीं हैं और इंगित करता है कि इन ग्रेडिएंट्स की गणना कैसे करें। उदाहरण के लिए, बीवर ट्रिपल्स(Beaver triples) ट्रिक का उपयोग करके किए जाने वाले गुणन के संबंध में, हम इस ट्रिक को अलग नहीं करना चाहते हैं कि गुणन को अलग करना बहुत आसान हो: $\partial_b (a \cdot b) = a \cdot \partial b$। यहाँ हम उदाहरण के लिए इन ग्रेडिएंट्स की गणना करने का तरीका बताते हैं:

class MulBackward(GradFunc):
    def __init__(self, self_, other):
        super().__init__(self, self_, other)
        self.self_ = self_
        self.other = other

    def gradient(self, grad):
        grad_self_ = grad * self.other
        grad_other = grad * self.self_ if type(self.self_) == type(self.other) else None
        return (grad_self_, grad_other)

यदि आप यह देखने के लिए उत्सुक हैं कि हमने और अधिक ग्रेडिएंट कैसे लागू किए हैं, तो आप tensors/interpreters/gradients.py पर एक नज़र डाल सकते हैं।

अभिकलन ग्राफ के संदर्भ में, इसका मतलब है कि ग्राफ की एक प्रति स्थानीय बनी हुई है और जो सर्वर फॉरवर्ड पास का समन्वय करता है वह बैकवर्ड पास कैसे करें, इस पर भी निर्देश प्रदान करता है। यह हमारी सेटिंग में पूरी तरह से मान्य परिकल्पना है।

2.3 सुरक्षा की गारंटी

अंतिम, आइए हम यहां प्राप्त होने वाली सुरक्षा के बारे में कुछ संकेत देते हैं: विरोधी जो हम यहां पर विचार कर रहे हैं वह है ईमानदार लेकिन जिज्ञासु: इसका मतलब यह है कि एक विरोधी यह प्रोटोकॉल दौड़कर डेटा के बारे में कुछ भी नहीं सीख सकता है, लेकिन एक दुर्भावनापूर्ण विरोधी अभी भी प्रोटोकॉल से विचलित हो सकता है और उदाहरण के लिए गणना को तोड़फोड़ करने के लिए शेयरों को भ्रष्ट करने की कोशिश करता है। निजी तुलना सहित SMPC संगणना में दुर्भावनापूर्ण विरोधी के खिलाफ सुरक्षा अभी भी एक खुली समस्या है।

इसके अलावा, भले ही सिक्योर मल्टी-पार्टी कम्प्यूटेशन यह सुनिश्चित करता है कि प्रशिक्षण डेटा एक्सेस नहीं किया गया था, सादे पाठ की दुनिया के कई खतरे अभी भी यहां मौजूद हैं। उदाहरण के लिए, जैसा कि आप मॉडल से अनुरोध कर सकते हैं (MLaaS के संदर्भ में), आप भविष्यवाणियां प्राप्त कर सकते हैं जो प्रशिक्षण डेटासेट के बारे में जानकारी का खुलासा कर सकती है। विशेष रूप से आपको सदस्यता हमलों(membership attacks) के खिलाफ कोई सुरक्षा नहीं है, मशीन सीखने की सेवाओं पर एक आम हमला जहां विपक्षी यह निर्धारित करना चाहता है कि क्या डेटासेट में एक विशिष्ट वस्तु का उपयोग किया गया था। इसके अलावा, अन्य हमले जैसे कि अनपेक्षित मेमोराइजेशन प्रक्रियाएं (डेटा आइटम के बारे में विशिष्ट विशेषता सीखने वाले मॉडल), मॉडल उलटा या निष्कर्षण अभी भी संभव है।

एक सामान्य समाधान जो ऊपर बताए गए कई खतरों के लिए प्रभावी है, वह है डिफरेंशियल प्राइवेसी(Differential privacy) को जोड़ना। इसे सुरक्षित रूप से बहु-पक्षीय संगणना के साथ जोड़ा जा सकता है और यह बहुत ही रोचक सुरक्षा गारंटी प्रदान कर सकता है। वर्तमान में हम कई कार्यान्वयन पर काम कर रहे हैं और एक उदाहरण प्रस्तुत करने की उम्मीद करते हैं जो जल्द ही दोनों को जोड़ती है!

निष्कर्ष

जैसा कि आपने देखा, एसएमपीसी का उपयोग कर एक मॉडल को प्रशिक्षित करना एक कोड बिंदु से जटिल नहीं है, यहां तक ​​कि हम हुड के नीचे जटिल वस्तुओं का उपयोग करते हैं। इसे ध्यान में रखते हुए, अब आपको अपने उपयोग-मामलों का विश्लेषण करना चाहिए कि प्रशिक्षण या मूल्यांकन के लिए एन्क्रिप्टेड संगणना की आवश्यकता है या नहीं। यदि एन्क्रिप्टेड संगणना सामान्य रूप से बहुत धीमी है, तो इसका उपयोग सावधानीपूर्वक भी किया जा सकता है ताकि समग्र गणना उपरि को कम किया जा सके।

यदि आपने इसका आनंद लिया और एआई और एआई आपूर्ति श्रृंखला (डेटा) के विकेन्द्रीकृत स्वामित्व के संरक्षण, गोपनीयता की ओर आंदोलन में शामिल होना चाहते हैं, तो आप निम्न तरीकों से ऐसा कर सकते हैं!

Pysyft को Github पर Star करें!

हमारे समुदाय की मदद करने का सबसे आसान तरीका रिपॉजिटरी को अभिनीत करना है! यह हमारे द्वारा बनाए जा रहे कूल टूल्स के बारे में जागरूकता बढ़ाने में मदद करता है।

GitHub पर हमारे ट्यूटोरियल उठाओ!

हमने फेडरेटेड और प्राइवेसी-प्रिजर्विंग लर्निंग की बेहतर समझ पाने के लिए वास्तव में अच्छा ट्यूटोरियल बनाया और ऐसा होने के लिए हम ईंटों का निर्माण कर रहे हैं।

हमारे Slack में शामिल हों!

नवीनतम प्रगति पर अद्यतित रहने का सबसे अच्छा तरीका हमारे समुदाय में शामिल होना है!

एक कोड परियोजना में शामिल हों!

हमारे समुदाय में योगदान करने का सबसे अच्छा तरीका एक कोड योगदानकर्ता बनना है! यदि आप "one time" मिनी-प्रोजेक्ट्स शुरू करना चाहते हैं, तो आप PySyft GitHub जारी करने वाले पृष्ठ पर जा सकते हैं और 'अच्छा पहला अंक' चिह्नित मुद्दों की खोज कर सकते हैं।

दान करना

यदि आपके पास हमारे कोडबेस में योगदान करने का समय नहीं है, लेकिन फिर भी समर्थन उधार देना चाहते हैं, तो आप हमारे ओपन कलेक्टिव में भी एक बैकर बन सकते हैं। सभी दान हमारी वेब होस्टिंग और अन्य सामुदायिक खर्चों जैसे कि हैकाथॉन और मीटअप की ओर जाते हैं!


In [ ]: